#include "rockarray.hh"

#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <algorithm>

RockArray::RockArray(const std::string& filepath) :
    _rocks{}
{
    std::ifstream infile {filepath};
    std::string line {};
    while (std::getline(infile, line))
        this->_rocks.emplace_back(std::move(line));
}

void RockArray::print() const
{
    for (const std::string& line : this->_rocks)
        std::cout << line << std::endl;
}

void RockArray::rollNorth()
{
    std::size_t rowSize {this->_rocks.size()};
    std::size_t colSize {this->_rocks.front().size()};

    for (std::size_t col = 0; col < colSize; ++col)
    {
        for (std::size_t row = 0; row < rowSize; ++row) 
        {
            if (this->_rocks[row][col] == 'O')
                this->rollRoundedRocksNorth(row, col);
        }
    }
}

void RockArray::rollSouth()
{
    std::size_t rowSize {this->_rocks.size()};
    std::size_t colSize {this->_rocks.front().size()};

    for (std::size_t col = 0; col < colSize; ++col)
    {
        for (std::size_t row = rowSize - 1; row >= 0; --row)
        {
            if (this->_rocks[row][col] == 'O')
                this->rollRoundedRocksSouth(row, col);
            if (row == 0) break;
        }
    }
}

void RockArray::rollWest()
{
    std::size_t rowSize {this->_rocks.size()};
    std::size_t colSize {this->_rocks.front().size()};

    for (std::size_t row = 0; row < rowSize; ++row) 
    {
        for (std::size_t col = 0; col < colSize; ++col)
        {
            if (this->_rocks[row][col] == 'O')
                this->rollRoundedRocksWest(row, col);
        }
    }
}

void RockArray::rollEast()
{
    std::size_t rowSize {this->_rocks.size()};
    std::size_t colSize {this->_rocks.front().size()};

    for (std::size_t row = 0; row < rowSize; ++row) 
    {
        for (std::size_t col = colSize - 1; col >= 0; --col)
        {
            if (this->_rocks[row][col] == 'O')
                this->rollRoundedRocksEast(row, col);
            if (col == 0) break;
        }
    }
}

void RockArray::rollRoundedRocksNorth(std::size_t row, std::size_t col)
{
    // Check this->_rocks[row - 1][col] ==> ...
    while (true)
    {
        if (row == 0) break;
        if (this->_rocks[row - 1][col] == '.')
        {
            // slide 
            this->_rocks[row - 1][col] = 'O';
            this->_rocks[row][col] = '.';
            row -= 1;
        }
        else
            break;
    }
}

void RockArray::rollRoundedRocksSouth(std::size_t row, std::size_t col)
{
    std::size_t rowSize {this->_rocks.size()};
    while (true)
    {
        if (row == rowSize - 1) break;
        if (this->_rocks[row + 1][col] == '.')
        {
            // slide 
            this->_rocks[row + 1][col] = 'O';
            this->_rocks[row][col] = '.';
            row += 1;
        }
        else
            break;
    }
}

void RockArray::rollRoundedRocksWest(std::size_t row, std::size_t col)
{
    while (true)
    {
        if (col == 0) break;
        if (this->_rocks[row][col - 1] == '.')
        {
            // slide 
            this->_rocks[row][col - 1] = 'O';
            this->_rocks[row][col] = '.';
            col -= 1;
        }
        else
            break;
    }
}

void RockArray::rollRoundedRocksEast(std::size_t row, std::size_t col)
{
    std::size_t colSize {this->_rocks.front().size()};
    while (true)
    {
        if (col == colSize - 1) break;
        if (this->_rocks[row][col + 1] == '.')
        {
            // slide 
            this->_rocks[row][col + 1] = 'O';
            this->_rocks[row][col] = '.';
            col += 1;
        }
        else
            break;
    }
}

std::size_t RockArray::getTotalLoad() const
{
    std::size_t sum {0};
    std::size_t rowSize {this->_rocks.size()};

    for (std::size_t r = 0; r < rowSize; ++r)
    {
        const std::string& line {this->_rocks[r]};
        std::size_t roundRockSize {
            static_cast<std::size_t>(std::count(line.cbegin(), line.cend(), 'O'))
        };

        std::size_t rowValue {rowSize - r};

        sum += rowValue * roundRockSize;
    }
    
    return sum;
}

RockArray RockArray::clone() const
{
    return RockArray{*this};
}

bool operator == (const RockArray& r1, const RockArray& r2)
{
    return r1._rocks == r2._rocks;
}